package org.aeon.service.impl;


import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.aeon.config.ServerConfig;
import org.aeon.dao.UserDao;
import org.aeon.entity.User;
import org.aeon.entity.dto.UserDto;
import org.aeon.message.Message;
import org.aeon.service.UserService;
import org.aeon.utils.*;
import org.apache.ibatis.session.SqlSession;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

@Slf4j
public class UserServiceImpl implements UserService {


    @Override
    public User login(String username, String password) {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            return userDao.login(username, password);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    @Override
    public void userOffline(Long userId) {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            // 设置退出时间
            userDao.updateUserTime(userId, new Date());
        }
    }

    @Override
    public Long getUserId(String username) {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            return userDao.getIdByUsername(username);
        }
    }

    @Override
    public User getUserById(Long userId) {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            return userDao.getById(userId);
        }
    }

    @Override
    public boolean isUserExist(Long userId) {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            return userDao.isExist(userId) > 0;
        }
    }

    @Override
    public User register(User user) {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            user.setCreationTime(new Date());
            final int i = userDao.insert(user);
            if (i == 1) {
                updateUserCache(user.getId());
                return user;
            }
            return null;
        }
    }

    @Override
    public UserDto getSimpleUser(Long userId) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final String s = jedis.get(userId.toString() + "-info");
            // 尝试redis获取
            if (s != null) {
                return new Gson().fromJson(s, UserDto.class);
            } else {
                // 数据库读取
                if (updateUserCache(userId)) {
                    // 刷新成功重新获取
                    return getSimpleUser(userId);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    @Override
    public List<UserDto> getBatchUserInfo(List<Long> userIds) {
        // 使用并行流处理用户 ID
        checkUserCache(userIds, "-info");
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            // 使用管道
            final Pipeline p = jedis.pipelined();
            final Gson gson = new Gson();
            final List<Response<String>> list = userIds.stream().map(userId -> {
                final String key = userId.toString() + "-info";
                return p.get(key);
            }).toList();
            // 管道的 sync 方法确保所有命令都被发送，并等待所有命令的响应
            p.sync();

            return list.stream()
                    .map(Response::get)
                    .map(item -> gson.fromJson(item, UserDto.class))
                    .toList();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return new ArrayList<>();
    }

    @Override
    public void checkUserCache(List<Long> userIds, String suffix) {
        userIds.parallelStream().forEach(userId -> {
            try (Jedis jedis = RedisPoolUtils.getJedis()) {
                final String key = userId.toString() + suffix;
                if (!jedis.exists(key)) {
                    updateUserCache(userId);
                }
            }
        });
    }

    @Override
    public boolean updateUserCache(Long... userIds) {
        try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            try (final Jedis jedis = RedisPoolUtils.getJedis()) {
                final Gson gson = new Gson();
                // 更新 user-id-all
                final List<Long> userAllId = userDao.getAllId();
                jedis.set("user-id-all", gson.toJson(userAllId));
                jedis.expire("user-id-all", ServerConfig.getUserAllTimeoutSeconds());

                if (userIds == null || userIds.length == 0) {
                    // 更新所有
                    final List<User> users = userDao.getAll();
                    batchUpdates(jedis, users);
                } else if (userIds.length == 1) {
                    // 单独更新 userId字段
                    final User user = userDao.getById(userIds[0]);
                    final String key = userIds[0] + "-info";
                    jedis.set(key, gson.toJson(user));
                    jedis.expire(key, RandomUtils.random(ServerConfig.getUserInfoTimeoutSeconds(), 0.99, 0.8));
                } else {
                    // 指定数量更新
                    final List<User> users = userDao.getByIds(Arrays.stream(userIds).toList());
                    batchUpdates(jedis, users);
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
            return true;
        }
    }

    @Override
    public List<UserDto> fuzzySearch(String keyword) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            // 尝试redis获取
            final String userIdSrt = jedis.get("user-id-all");
            if (userIdSrt != null) {
                final List<Long> userIds = JsonUtils.fromJsonArray(userIdSrt, Long.class);
                checkUserCache(userIds, "-info");
                return userIds.parallelStream()
                        .map(userId -> {
                            try (final Jedis j = RedisPoolUtils.getJedis()) {
                                final Gson gson = new Gson();
                                return gson.fromJson(j.get(userId + "-info"), UserDto.class);
                            }
                        })
                        .filter(userDto -> userDto.getUsername().contains(keyword) || userDto.getNickname().contains(keyword))
                        .toList();
            } else {
                // 数据库读取
                if (updateUserCache()) {
                    // 刷新成功重新获取
                    return fuzzySearch(keyword);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return new ArrayList<>();
    }

    @Override
    public boolean addMessageToQueue(Long userId, Message message) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final Gson gson = new Gson();
            jedis.rpush(userId.toString() + "-msg-queue",
                    message.getMessageType() + ":" + gson.toJson(message));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
        return true;
    }

    @Override
    public Message getMessage(Long userId) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final Gson gson = new Gson();
            final String pop = jedis.lpop(userId.toString() + "-msg-queue");
            if (pop == null || "nil".equals(pop)) return null;
            final String[] split = pop.split(":", 2);
            return (Message) gson.fromJson(split[1], Message.getMessageClass(Integer.parseInt(split[0])));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    @Override
    public List<Message> getAllMessages(Long userId) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final List<String> range = jedis.lrange(userId.toString() + "-msg-queue", 0, -1);
            final List<Message> messages = new ArrayList<>();
            if (range.isEmpty()) return messages;
            final Gson gson = new Gson();
            range.forEach((item) -> {
                final String[] split = item.split(":", 2);
                messages.add((Message) gson.fromJson(split[1], Message.getMessageClass(Integer.parseInt(split[0]))));
            });
            return messages;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    @Override
    public String registerToken(Long userId, String username, String password) {
        final String token = UUIDUtils.generateUniqueId(username, password);
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final String key = userId.toString() + "-token";
            jedis.set(key, token);
            jedis.expire(key, ServerConfig.getTokenTimeoutSeconds());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
        return token;
    }

    @Override
    public String checkTokens(Long userId) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final String key = userId.toString() + "-token";
            if (!jedis.exists(key)) return null;
            final long ttl = jedis.ttl(key);
            // 延长存在时间
            if (ServerConfig.getTokenResetTriggerFactor() == 1 ||
                    ttl <= ServerConfig.getTokenTimeoutSeconds() * ServerConfig.getTokenResetTriggerFactor()) {
                jedis.expire(key, ServerConfig.getTokenTimeoutSeconds());
            }
            return jedis.get(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    @Override
    public void delTokens(Long userId) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final String key = userId.toString() + "-token";
            jedis.del(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    private void batchUpdates(Jedis jedis, List<User> users) {
        final Gson gson = new Gson();
        // 更新全部 userId字段
        // 使用管道 (pipeline) 进行批量设置
        final Pipeline p = jedis.pipelined();
        users.forEach(userDto -> {
            final String key = userDto.getId().toString() + "-info";
            p.set(key, gson.toJson(userDto));
            p.expire(key, RandomUtils.random(ServerConfig.getUserInfoTimeoutSeconds(), 0.99, 0.8));
        });
        // 管道的 sync 方法确保所有命令都被发送，并等待所有命令的响应
        p.sync();
    }

}
